use core::cell::Cell;
use hicollections::{list, List, ListNode};

//
// 提供底层的IO事件监测功能，包括如下接口
// 1. 定义三种事件: POLLIN/POLLOUT/POLLET
// 2. 定义struct Poll，提供如下接口
// 2.1 fn new<T: Pool>(pool: &T) -> Result<PBox<'_, Scheduler>, Error>
// 2.2 fn add(&mut self, fd: i32, events: u32, e: *const Event) -> Result<(), Error>
// 2.3 fn mod(&mut self, fd: i32, events: u32, e: *const Event) -> Result<(), Error>
// 2.3 fn del(&mut self, fd: i32) -> Result<(), Error>
// 2.4 fn wait<F>(&mut self, timeout: i32, f: F) -> Result<(), Error>
//     where F: FnMut(events: u32, e: *const Event),
// 其中timeout = -1, 表示一直等待
// timeout = 0, 表示立即返回
// 其他是等待的具体超时时间，单位是microsecond
cfg_if::cfg_if! {
    if #[cfg(any(
            target_os = "linux",
            target_os = "android",
            target_os = "illumos",
            target_os = "redox",
    ))] {
        mod epoll;
        pub use epoll::*;
    } else if #[cfg(any(
            target_os = "ios",
            target_os = "macos",
            target_os = "freebsd",
            target_os = "openbsd",
            target_os = "netbsd",
            target_os = "dragonfly",
            target_os = "tvos",
            target_os = "watchos",
            ))] {
        mod kqueue;
        pub use kqueue::*;
    } else {
        mod select;
        pub use select::*;
    }
}

/// 基于底层的Poll实现支持定时器和无IO的事件分发功能
mod scheduler;
pub use scheduler::*;

mod waker;
pub(crate) use waker::*;

mod timer;
pub use timer::*;

#[repr(C)]
pub struct Event {
    pub(crate) node: ListNode,
    events: Cell<u32>,
    handle: fn(&Event, u32, &mut Scheduler),
}

pub fn event_list_new() -> List<Event> {
    list!(Event, node)
}

impl Event {
    pub fn new(handle: fn(&Event, u32, &mut Scheduler)) -> Self {
        Self {
            node: ListNode::new(),
            events: Cell::new(0),
            handle,
        }
    }

    pub fn handle(&self, event: u32, sched: &mut Scheduler) {
        (self.handle)(self, event, sched)
    }
}

#[cfg(test)]
mod test {
    use crate::event::{self, Scheduler};
    use core::ptr;
    use hipool::MemPool;
    #[test]
    fn test_waker() {
        let pool = MemPool::new_boxed(0).unwrap();
        let mut poll = event::Poll::new_in(unsafe { &*pool.as_ptr() }).unwrap();
        let waker = event::Waker::new().unwrap();
        let _ = poll.add_event(waker.fd, event::POLLIN, ptr::null::<event::Event>());
        let mut events = 0;
        let n = poll
            .wait(0, |e, _| {
                events = e;
            })
            .unwrap();
        assert_eq!(events, 0);
        assert_eq!(n, 0);
        assert!(!waker.awaken());
        waker.wake();
        let n = poll
            .wait(0, |e, _| {
                events = e;
            })
            .unwrap();
        assert_eq!(n, 1);
        assert!(waker.awaken());
    }

    #[test]
    fn test_scheduler() {
        let pool = MemPool::new_boxed(0).unwrap();
        let mut sched = event::Scheduler::new_in(unsafe { &*pool.as_ptr() }).unwrap();
        fn timer_timeout(this: &event::Timer, sched: &mut Scheduler) {
            static mut STOP: i32 = 0;
            if unsafe { STOP } == 0 {
                unsafe { STOP = 1 };
                unsafe { sched.set_timer(this, 1000000) };
            } else {
                sched.stop();
            }
        }
        let timer = event::Timer::new(timer_timeout);
        let now = sched.now();
        unsafe { sched.set_timer(&timer, 1 * 1000 * 1000) };
        sched.run();
        assert!(sched.stopped());
        let cur = sched.now();
        assert!((cur - now).as_micros() >= 2 * 1000 * 1000);
    }
}
